﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Xml;
using System.Xml.Linq;

namespace CIP4.RulesEngine
{
    public class SetAtomType : AtomType
    {
        public IList<object> AtomValue { get; set; }

        public override object AtomObjectValue
        {
            get
            {
                return AtomValue;
            }
        }

        public override Common.CIP4PrimitiveType UnderlyingType
        {
            get { return ElementType.UnderlyingType; }
        }

        public override bool IsLiteral
        {
            get { return AtomSource == AtomSourceEnum.literal; }
        }

        public override bool IsNamedValue
        {
            get { return AtomSource == AtomSourceEnum.namedValue; }
        }

        public AtomType ElementType { get; set; }

        public SetAtomType()
        {
        }

        public SetAtomType(IList<object> value)
        {
            AtomValue = value;
            AtomSource = AtomSourceEnum.literal;
        }

        public SetAtomType(AtomSourceEnum atomSource, string atomName)
            : base(atomSource, atomName)
        {
        }

        public SetAtomType(XElement element)
            : base(element)
        {
            if (element.Name != XName.Get("SetAtom", RuleXmlNS))
                throw new ArgumentException(string.Format("{0} is not a valid xml for a set atom", element));
            AtomValue = new List<object>();
            foreach (XElement el in element.Elements())
                AtomValue.Add(Initialize(el));
        }

        public override CIValueType Clone()
        {
            if (AtomSource == AtomSourceEnum.literal)
                return new SetAtomType(AtomValue); // if it's a literal it should have a value
            return new SetAtomType(AtomSource, AtomName);
        }

        public override object Evaluate(Common.IDataPacketType data)
        {
            if (AtomSource == AtomSourceEnum.literal)
                return AtomObjectValue;
            if (AtomSource == AtomSourceEnum.namedValue)
                return data.GetNamedValue(AtomName);
            if (AtomSource == AtomSourceEnum.parameter)
            {
                if (data.Parameters.ContainsKey(AtomName))
                    return data.Parameters[AtomName];
                return new object[] {};
            }
            if (AtomSource == AtomSourceEnum.function)
            {
                if (data.GetValue == null)
                    throw new ArgumentException("Rule references a get value function but none was supplied - you must provide a function for the Execute Data parameter's GetValue function");
                return data.GetValue(AtomName, data);
            }
            throw new ApplicationException("Invalid rule unknown Atom type encountered");
        }

        public override void XmlFor(XmlWriter xwrtr, bool writeNamespace)
        {
            xwrtr.WriteStartElement("SetAtom");
            if (writeNamespace)
                xwrtr.WriteAttributeString("xmlns", RuleXmlNS);
            XmlFor(xwrtr);
            if (AtomValue != null)
                foreach (object value in AtomValue)
                {
                    ((AtomType)value).XmlFor(xwrtr, false);
                }
            xwrtr.WriteEndElement();
        }


        public override string ToString(ExpressionFormatEnum format)
        {
            if (AtomSource == AtomSourceEnum.literal)
            {
                string text = string.Empty;
                string delimiter = string.Empty;
                object value = base.Evaluate(new RulesEngineDataPacket());
                foreach(object instance in ((IList)value))
                {
                    text += delimiter + instance;
                    delimiter = ",";
                }
                return text;
            }
            if (AtomSource == AtomSourceEnum.namedValue)
                return AtomName;
            if (AtomSource == AtomSourceEnum.parameter)
                return "Parameter." + AtomName;
            if (AtomSource == AtomSourceEnum.function)
                return "Call " + AtomName + "()";
            if (AtomSource == AtomSourceEnum.variable)
                return "Call XPath( " + AtomName + ")";
            return "InvalodOp";
        }

    }
}
